/*
 * MinimaxingConnectNPlayer.cpp
 *
 *  Created on: Feb 7, 2012
 *      Author: sd
 */

#include "MinimaxingConnectNPlayer.h"
#include <iostream>

using std::cerr;
MinimaxingConnectNPlayer::MinimaxingConnectNPlayer(int height1, int width1, int numToWin1, int debug1)	: ConnectNPlayer(height1, width1, numToWin1, debug1)   {
	step = 0;
}

int MinimaxingConnectNPlayer::calcMove() {
	step = 0;
	depth = 0;
	vector<int> actions = getValidActions();
	float bestMove=actions[0], bestVal=0, val;
	for (unsigned int k=0; k<actions.size(); k++) {
		//val = calcHeuristic(actions[k], true);
		val = calcMinimaxValue2(actions[k], true, 4, 0.0);
		//cerr << "h("<<actions[k]<<")="<<val<<"\n";

		if (val==1.0) {bestMove = actions[k]; bestVal = val;break;}
		if (val>bestVal) {bestMove = actions[k]; bestVal = val;};
	}
	return bestMove;
}

float MinimaxingConnectNPlayer::calcMinimaxValue2(int action, bool myTurn, int maxDepth, int b) {
	step++;
	if (isTerminateState(action,myTurn))
		return myTurn ? 1.0 : 0.0;
	addDib(action,myTurn);
	float val, curBestValue = myTurn ? 1.0 : 0.0;
	vector<int> actions = getValidActions();
	if (actions.size()==0) curBestValue = 0.5;
	else
		for (unsigned int k=0; k<actions.size(); k++) {
			val = maxDepth>0 ?
				calcMinimaxValue2(actions[k],!myTurn,maxDepth--,curBestValue):
				calcHeuristic(actions[k],!myTurn);//no !
			if (myTurn && val<curBestValue) {
				curBestValue = val;
				if (val<=b) break;
			} else if (!myTurn && val>curBestValue) {
				curBestValue = val;
				if (val>=b) break;
			}
		}
	//printBoard();
	removeDib(action);

	//for (int k=maxDepth; k>=-1;k--) cerr << "    ";

	//cerr << "curBestValue="<<curBestValue<<"\n";
	return curBestValue;
}



/*
float MinimaxingConnectNPlayer::calcNextMinimaxValue(int action, bool myTurn, int maxDepth) {
	if (isTerminateState(action,myTurn)) {
		return myTurn ? 1.0 : 0.0;
	}
	addDib(action,myTurn);
	float value = calcMinimaxValue(!myTurn, maxDepth);
	removeDib(action);
	return value;
}*/





float  MinimaxingConnectNPlayer::calcProbabilityToBuildAtLeastOneLine(int lastUpdatedCol) {
    int x0 = lastUpdatedCol, y0 = heights[x0]-1;
    bool s = board[x0][y0];
    int x,y, xmin, ymin, xmax, ymax;

    //printBoard();

    ProbCalculator pc = ProbCalculator(numToWin);
    //cerr<< "bottom to top\n";
    //Check Bottom to Top
    ymin = y0-numToWin > -1 ? y0-numToWin : -1;
    ymax = y0+numToWin < height ? y0+numToWin : height;
    for (y=y0-1; y>ymin && board[x0][y]==s; y--);
    for (y=y+1; y<ymax; y++){
    	pc.push(y<=y0 ? -1 : 0);}
    if (pc.getProb()==1.0) return 1.0;
    pc.changeDirection();

    //cerr<< "left to right\n";
    //Check Left to Right
    xmin = x0-numToWin > -1 ? x0-numToWin : -1;
    xmax = x0+numToWin < width ? x0+numToWin : width;
    for (x=x0-1; x>xmin && (board[x][y0]==s || y0>=heights[x]); x--);
    for (x=x+1;  x<xmax && (board[x][y0]==s || y0>=heights[x]); x++) {
    	//cerr << "x="<<x<<", heights[x]="<<heights[x]<<",y0="<<y0<<"\n";

    	pc.push(y0-heights[x]);}
    if (pc.getProb()==1.0) return 1.0;
    pc.changeDirection();

    //cerr<< "bottom-left to top-right\n";
    //Check bottom-left to top-right
    for (x=x0-1, y=y0-1; x>xmin && y>ymin && (board[x][y]==s || y>=heights[x]); x--, y--);
    for (x=x+1,  y=y+1;  x<xmax && y<ymax && (board[x][y]==s || y>=heights[x]); x++, y++)
    	pc.push(y-heights[x]);
    if (pc.getProb()==1.0) return 1.0;
    pc.changeDirection();

    //cerr<< "top-left to bottom-right\n";
    //Check top-left to bottom-right
    for (x=x0-1, y=y0+1; x>xmin && y<ymax && (board[x][y]==s || y>=heights[x]); x--, y++);
    for (x=x+1,  y=y-1;  x<xmax && y>ymin && (board[x][y]==s || y>=heights[x]); x++, y--)
    	pc.push(y-heights[x]);

    float pp = pc.getProb();
    //cerr <<"s=" <<(s?'X':'O')<< ", prob="<<pp << "\n";
    return pp;
}

float MinimaxingConnectNPlayer::calcHeuristic(int action, bool myTurn) {
	//cerr <<"------- trying action: " <<action <<"\n";

	addDib(action,myTurn);
	float pX = calcProbabilityToBuildAtLeastOneLine(action);
	removeDib(action);
	addDib(action,!myTurn);
	float pO = calcProbabilityToBuildAtLeastOneLine(action);
	removeDib(action);
	float h = pX>pO ? 0.9*pX*pX+0.1 : 0.899*pO*pO+0.1;// + 0.1 + 0.85*(1-pX)*pO;
	return myTurn ? h : 1-h;
}
